import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;


public class Parser {
	private final int MAX_NODES = 55;
	private Tokenizer TOKENIZER = new Tokenizer();
	private ArrayList<Token> token_array = new ArrayList<Token>();
	private HashMap<Integer, ParserNode> PT = new HashMap<Integer, ParserNode>();
	private int token_index = 0;
	private int current_row = 1;
	private int next_row = 1;
	
	public void parseProgram (String file) {
		try {
			BufferedReader reader = new BufferedReader(new FileReader(file));
		} catch (FileNotFoundException e) {}
		
		token_array = TOKENIZER.tokenize(file);		
		
		// Call sub functions based on token type
		while (token_index < token_array.size()) {
			Tokens current_tok = token_array.get(token_index).getNumber();
			
			// Check to make sure PROGRAM is first token
			if (current_tok == Tokens.PROGRAM) {
				prog();
			}
			else {
				System.out.println("ERROR: Program must be the first token of the input program");
			}
			
			// Check for end token 
			current_tok = token_array.get(token_index).getNumber();
			if (current_tok != Tokens.END) {
				System.out.println("ERROR: Missing END token at the end of the program");
			}

		}
	}
	
	public void prog() {
		int myRow = current_row;
		current_row++;
		token_index++;
		int declSeqRow = declSeq();

		// Move to next token
		token_index++;
		Tokens current_tok = token_array.get(token_index).getNumber();
		
		// Check to make sure BEGIN follows
		if (current_tok == Tokens.BEGIN) {
			token_index++;
		}
		else {
			System.out.println("ERROR: Expected BEGIN token missing");
		}
		
		// Parse statement sequence
		int stmtSeqRow = stmtSeq();
		
		// Check to make sure END follows
		token_index++;
		current_tok = token_array.get(token_index).getNumber();

		if (current_tok == Tokens.END) {
			token_index++;
		}
		else {
			System.out.println("ERROR: Expected END token missing");
		}
		
		// Add row to parse tree
		PT.put(myRow, new ParserNode("<prog>", 1, declSeqRow, stmtSeqRow, 0));
		
	}
	
	public int declSeq() {
		int myRow = current_row;
		int alternative = 0;
		current_row++;
		
		int declRow = decl();
		
		// Check to see which alternative is used
		Tokens current_tok = token_array.get(token_index).getNumber();
		if (current_tok == Tokens.INT) {
			int declRow2 = decl();
			PT.put(myRow, new ParserNode("<decl_stmt>", 2, declRow, declRow2, 0));
		}
		else {
			// Add row to parse tree
			PT.put(myRow, new ParserNode("<decl_stmt>", 1, declRow, 0, 0));
		}

		return myRow;
	}
	
	public int stmtSeq() {
		int myRow = current_row;
		int alternative = 0;
		current_row++;
		
		int stmtRow = stmt();
		
		// Check to see if alternative is used
		Tokens current_tok = token_array.get(token_index).getNumber();

		if (current_tok == Tokens.IF || current_tok == Tokens.READ || current_tok == Tokens.WRITE || current_tok == Tokens.WHILE || current_tok == Tokens.IDENTIFIER) {
			alternative = 2;
			
			int stmtSeqRow = stmtSeq();
			PT.put(myRow, new ParserNode("<stmt_seq>", alternative, stmtRow, stmtSeqRow, 0));			
		}
		else {
			alternative = 1;
			PT.put(myRow, new ParserNode("<stmt_seq>", alternative, stmtRow, 0, 0));			
		}
		
		return myRow;
	}
	
	public int decl() {
		int myRow = current_row;
		current_row++;
		Tokens current_tok = token_array.get(token_index).getNumber();
		
		// Check to make sure INT follows
		if (current_tok == Tokens.INT) {
			token_index++;
		}
		else {
			System.out.println("ERROR: Expected INT token for declaration missing");
		}
		
		int idListRow = idList();
		
		// Add row to parse tree
		PT.put(myRow, new ParserNode("<decl>", 1, idListRow, 0, 0));

		return myRow;
	}
	
	public int idList() {
		int myRow = current_row;
		current_row++;
		int alternative = 0;
		
		Tokens current_tok = token_array.get(token_index).getNumber();
		if (current_tok == Tokens.IDENTIFIER) {
			token_index++;
		}
		else {
			System.out.println("ERROR: Expected identifier missing");
		}
		
		int idRow = id();
		
		// Check to see if alternative is used
		token_index++;
		current_tok = token_array.get(token_index).getNumber();
		
		if (current_tok == Tokens.COMMA) {
			token_index++;
			int idListRow = idList();
			
			// Add row to parse tree
			PT.put(myRow, new ParserNode("<id_list>", 2, idRow, idListRow, 0));

		}
		else {
			// Add row to parse tree
			PT.put(myRow, new ParserNode("<id_list>", 1, idRow, 0, 0));

		}
		
		return myRow;	
	}
	
	public int stmt() {
		int myRow = current_row;
		int alternative = 0;
		current_row++;
		
		Tokens current_tok = token_array.get(token_index).getNumber();
		if (current_tok == Tokens.IF) {
			alternative = 2;
			token_index++;
			
			int ifRow = ifstmt();
			PT.put(myRow, new ParserNode("<stmt>", alternative, ifRow, 0, 0));
			
		}
		else if (current_tok == Tokens.READ) {
			alternative = 4;
			token_index++;
			
			int inRow = input();
			PT.put(myRow, new ParserNode("<stmt>", alternative, inRow, 0, 0));

		}
		else if (current_tok == Tokens.WRITE) {
			alternative = 5;
			token_index++;
			
			int outRow = output();
			PT.put(myRow, new ParserNode("<stmt>", alternative, outRow, 0, 0));

		}
		else if (current_tok == Tokens.WHILE) {
			alternative = 3;
			token_index++;
			
			int loopRow = loop();
			PT.put(myRow, new ParserNode("<stmt>", alternative, loopRow, 0, 0));

		}
		else {
			alternative = 1;
			int assignRow = assign();
			PT.put(myRow, new ParserNode("<stmt>", alternative, assignRow, 0, 0));

			
		}
		return myRow;
	}
	
	public int assign() {
		int myRow = current_row;
		current_row++;
		
		Tokens current_tok = token_array.get(token_index).getNumber();
		if (current_tok == Tokens.IDENTIFIER) {
			int idRow = id();
			
			// Check for equals token
			token_index++;
			current_tok = token_array.get(token_index).getNumber();

			if (current_tok == Tokens.EQUAL) {
				 token_index++;
				 int expRow = exp();
				 
				 // Check for expected semicolon
				 token_index++;
				 current_tok = token_array.get(token_index).getNumber();
				 
				 if (current_tok == Tokens.SEMICOLON) {
					PT.put(myRow, new ParserNode("<assign>", 1, idRow, expRow, 0));
				 }
				 else {
					 System.out.println("ERROR: Missing semicolon after assignment");
				 }

			}
			else {
				System.out.println("ERROR: Expected assignement operator is missing");
			}
		}
		else {
			System.out.println("ERROR: Expected identifier is missing");
		}
		
		return myRow;
	}
	
	public int ifstmt() {
		int myRow = current_row;
		current_row++;

		//Check for expected IF token
		Tokens current_tok = token_array.get(token_index).getNumber();

		if (current_tok == Tokens.IF) {
			token_index++;
			int condRow = cond();

			// Check for expected THEN token
			current_tok = token_array.get(token_index).getNumber();
			
			if (current_tok == Tokens.THEN) {
				token_index++;
				int stmtSeqRow = stmtSeq();
				
				current_tok = token_array.get(token_index).getNumber();
				if (current_tok == Tokens.END) {
					token_index++;
					
					//Check for expected semicolon token
					current_tok = token_array.get(token_index).getNumber();
					if (current_tok == Tokens.SEMICOLON) {
						token_index++;
						PT.put(myRow, new ParserNode("<if>", 1, condRow, stmtSeqRow, 0));
					}
					else {
						System.out.println("ERROR: Expected semicolon following END is missing");
					}
				}
				else if (current_tok == Tokens.ELSE) {
					token_index++;
					int stmtSeqRow2 = stmtSeq();
					
					// Check for expected END
					current_tok = token_array.get(token_index).getNumber();
					
					if (current_tok == Tokens.END) {
						token_index++;
						
						// Check for expected semicolon token
						current_tok = token_array.get(token_index).getNumber();
						if (current_tok == Tokens.SEMICOLON) {
							token_index++;
							PT.put(myRow, new ParserNode("<if>", 2, condRow, stmtSeqRow, stmtSeqRow2));
						}
						else {
							System.out.println("ERROR: Expected semicolon following END is missing");
						}
					}
					else {
						System.out.println("ERROR: Expected END for if-statement missing");
					}
					
				}
				else {
					System.out.println("ERROR: Expected END or ELSE identifier for IF statement missing");
				}
			}
		}
		else {
			System.out.println("ERROR: Expected IF token missing");
		}
		
		return myRow;
	}
	
	public int loop() {
		int myRow = current_row;
		current_row++;

		//Check for expected WHILE token
		Tokens current_tok = token_array.get(token_index).getNumber();
		
		if (current_tok == Tokens.WHILE) {
			token_index++;
			
			int condRow = cond();
			
			// Check for LOOP token
			current_tok = token_array.get(token_index).getNumber();
			if (current_tok == Tokens.LOOP) {
				token_index++;
				int stmtSeqRow = stmtSeq();
				
				// Check for END token
				current_tok = token_array.get(token_index).getNumber();
				if (current_tok == Tokens.END) {
					token_index++;
					
					// Check for semicolon token
					current_tok = token_array.get(token_index).getNumber();
					if (current_tok == Tokens.SEMICOLON) {
						token_index++;
						PT.put(myRow, new ParserNode("<loop>", 1, condRow, stmtSeqRow, 0));
					}
					else {
						System.out.println("ERROR: Missing semicolon after END token");
					}

				}
				else {
					System.out.println("ERROR: Missing END token for while loop");
				}

			}
			else {
				System.out.println("ERROR: Expected LOOP token missing");
			}

		}
		else {
			System.out.println("ERROR: Expected WHILE token for loop is missing");
		}
		
		return myRow;
	}
	
	public int input() {
		int myRow = current_row;
		current_row++;

		//Check for expected READ token
		Tokens current_tok = token_array.get(token_index).getNumber();
		if (current_tok == Tokens.READ) {
			 token_index++;
			 int idListRow = idList();
			PT.put(myRow, new ParserNode("<in>", 1, idListRow, 0, 0));
		}
		else {
			System.out.println("ERROR: Expected READ token missing");
		}
		
		return myRow;
	}
	
	public int output() {
		int myRow = current_row;
		current_row++;

		//Check for expected WRITE token
		Tokens current_tok = token_array.get(token_index).getNumber();
		if (current_tok == Tokens.WRITE) {
			 token_index++;
			 int idListRow = idList();
			 PT.put(myRow, new ParserNode("<out>", 1, idListRow, 0, 0));
		}
		else {
			System.out.println("ERROR: Expected WRITE token missing");
		}
		return myRow;
	}
	
	public int cond() {
		int myRow = current_row;
		current_row++;

		Tokens current_tok = token_array.get(token_index).getNumber();
		if (current_tok == Tokens.EXCLAMATION) {
			token_index++;
			int condRow = cond();
			 PT.put(myRow, new ParserNode("<cond>", 2, condRow, 0, 0));
		}
		else if (current_tok == Tokens.LEFT_BRACK) {
			token_index++;
			int condRow = cond();
			
			// Check for AND or OR operator
			current_tok = token_array.get(token_index).getNumber();
			if (current_tok == Tokens.AND_OP || current_tok == Tokens.OR_OP) {
				token_index++;
				int condRow2 = cond();
				
				// Check for closing bracket
				current_tok = token_array.get(token_index).getNumber();
				if (current_tok == Tokens.RIGHT_BRACK) {
					token_index++;
					PT.put(myRow, new ParserNode("<cond>", 3, condRow, condRow2, 0));
				}
				else {
					System.out.println("ERROR: Missing closing bracket");
				}
			}
			else {
				System.out.println("ERROR: Missing AND or OR operator for conditional");
			}

		}
		else {
			int compRow = comp();
			PT.put(myRow, new ParserNode("<cond>", 1, compRow, 0, 0));
		}
		
		return myRow;
	}
	
	public int comp () {
		int myRow = current_row;
		current_row++;

		// Check for expected open parentheses
		Tokens current_tok = token_array.get(token_index).getNumber();
		if (current_tok == Tokens.LEFT_PAREN) {
			token_index++;
			int opRow1 = op();
			int compopRow = compOp();
			int opRow2 = op();
			
			// Check for expected closing parentheses
			current_tok = token_array.get(token_index).getNumber();
			if (current_tok == Tokens.RIGHT_PAREN) {
				token_index++;
				PT.put(myRow, new ParserNode("<comp>", 1, opRow1, compopRow, opRow2));
			}
			else {
				System.out.println("ERROR: Missing closing parentheses");
			}
		}
		else {
			System.out.println("ERROR: Missing opening parentheses");
		}
		
		return myRow;
	}
	
	public int exp() {
		int myRow = current_row;
		current_row++;

		int facRow = fac();
		
		// Check which alternative used
		Tokens current_tok = token_array.get(token_index).getNumber();
		if (current_tok == Tokens.PLUS_OP) {
			token_index++;
			int expRow = exp();
			PT.put(myRow, new ParserNode("<exp>", 2, facRow, expRow, 0));
		}
		else if (current_tok == Tokens.MINUS_OP) {
			token_index++;
			int expRow = exp();
			PT.put(myRow, new ParserNode("<exp>", 3, facRow, expRow, 0));
		}
		else {
			PT.put(myRow, new ParserNode("<exp>", 1, facRow, 0, 0));
		}
		
		return myRow;
	}
	
	public int fac() {
		int myRow = current_row;
		current_row++;

		int opRow = op();
		
		// Check which alternative used
		Tokens current_tok = token_array.get(token_index).getNumber();
		if (current_tok == Tokens.STAR_OP) {
			token_index++;
			int facRow = fac();
			PT.put(myRow, new ParserNode("<fac>", 2, opRow, facRow, 0));
		}
		else {
			PT.put(myRow, new ParserNode("<fac>", 1, opRow, 0, 0));
		}
		
		return myRow;
	}
	
	public int op() {
		int myRow = current_row;
		current_row++;
		
		// Check which alternative is used
		Tokens current_tok = token_array.get(token_index).getNumber();
		if (current_tok == Tokens.LEFT_PAREN) {
			token_index++;
			int expRow = exp();
			
			// Check for expected closing parentheses
			current_tok = token_array.get(token_index).getNumber();
			if (current_tok == Tokens.RIGHT_PAREN) {
				token_index++;
				PT.put(myRow, new ParserNode("<op>", 3, expRow, 0, 0));
			}
			else {
				System.out.println("ERROR: Missing closing parentheses");
			}
		}
		else if (current_tok == Tokens.IDENTIFIER) {
			int idRow = id();
			PT.put(myRow, new ParserNode("<op>", 2, idRow, 0, 0));
		}
		else if (current_tok == Tokens.DIGIT) {
			PT.put(myRow, new ParserNode("<op>", 1, 0, 0, 0));
		}
		else {
			System.out.println("ERROR: Unexpected token for OP");
		}
		
		return myRow;

	}
	
	public int compOp ()  {
		int myRow = current_row;
		current_row++;
		
		// Check which alternative used
		Tokens current_tok = token_array.get(token_index).getNumber();
		if (current_tok == Tokens.NOT_EQUAL_OP) {
			token_index++;
			PT.put(myRow, new ParserNode("<comp_op>", 1, 0, 0, 0));
		}
		else if (current_tok == Tokens.EQUAL_OP) {
			token_index++;
			PT.put(myRow, new ParserNode("<comp_op>", 2, 0, 0, 0));
		}
		else if (current_tok == Tokens.LESSTHAN_OP) {
			token_index++;
			PT.put(myRow, new ParserNode("<comp_op>", 3, 0, 0, 0));
		}
		else if (current_tok == Tokens.GREATERTHAN_OP) {
			token_index++;
			PT.put(myRow, new ParserNode("<comp_op>", 4, 0, 0, 0));
		}
		else if (current_tok == Tokens.LESSTHAN_EQ_OP) {
			token_index++;
			PT.put(myRow, new ParserNode("<comp_op>", 5, 0, 0, 0));
		}
		else if (current_tok == Tokens.GREATERTHAN_EQ_OP) {
			token_index++;
			PT.put(myRow, new ParserNode("<comp_op>", 6, 0, 0, 0));
		}
		else {
			System.out.println("ERROR: Unexpected token for comparative operator");
		}
		
		return myRow;
	}
	
	public int id() {
		int myRow = current_row;
		current_row++;
		String id = token_array.get(token_index).getToken();
		
		// Check which alternative used
		if (id.length() == 1) {
			PT.put(myRow, new ParserNode("<id>", 1, 0, 0, 0));
		}
		else if (Character.isUpperCase(id.charAt(0)) && Character.isDigit(id.charAt(1)) && id.length() == 2) {
			PT.put(myRow, new ParserNode("<id>", 2, 0, 0, 0));
		}
		else {
			PT.put(myRow, new ParserNode("<id>", 3, 0, 0, 0));
		}
		token_index++;
		
		return myRow;
	}
	
}
